package org.ytor.core.sqlflow.config;

import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.ytor.core.sqlflow.BaseRepository;
import org.ytor.core.sqlflow.SqlFlowInitSupport;
import org.ytor.core.util.scanner.ClasspathScanner;
import org.ytor.core.util.scanner.Scanned;
import org.ytor.core.util.scanner.Type;

import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.*;

/**
 * created by yangtong on 2025/8/12 20:50:45
 * <br/>
 */
@Component
@Slf4j
public class RepositoryRegistrarPostProcessor implements BeanDefinitionRegistryPostProcessor,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {

    /**
     * 仓储代理类后缀
     */
    private static final String proxySuffix = "$$RepositoryProxy";
    /**
     * 仓储代理类初始化方法名称
     */
    private static final String proxyMethodInitName = "$$RepositoryInit";

    /**
     * repository类所在路径
     */
    private String repositoryLocation;

    /**
     * 资源加载器
     */
    private ResourceLoader resourceLoader;

    private AutowireCapableBeanFactory beanFactory;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.repositoryLocation = environment.getProperty("system.sql-flow.repository-location");
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 1) 扫描指定类路径下，所有BaseRepository子类
        List<Scanned> candidates = ClasspathScanner.scan(
                repositoryLocation,
                ClasspathScanner.is(Type.CLASS)
                        .and(ClasspathScanner.isAssignableTo(BaseRepository.class))
        );

        for (Scanned scanned : candidates) {
            try {
                Class<?> superType = ClassUtils.forName(scanned.className(), resourceLoader.getClassLoader());
                // 生成代理类
                Class<?> implType = generateSubclass(superType);

                RootBeanDefinition rbd = new RootBeanDefinition(implType);
                rbd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                rbd.setInitMethodName(proxyMethodInitName);
                String beanName = BeanDefinitionReaderUtils.generateBeanName(rbd, registry);
                registry.registerBeanDefinition(beanName, rbd);
            } catch (ClassNotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    }

    /**
     * 用 ByteBuddy 生成子类：实现抽象方法，委托给拦截器
     */
    private Class<?> generateSubclass(Class<?> superType) {
        DynamicType.Builder<?> b;
        // 如果是接口
        if (superType.isInterface()) {
            b = new ByteBuddy()
                    .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
                    .implement(superType)
                    .name(superType.getName() + proxySuffix)
                    .modifiers(Visibility.PUBLIC);
        }
        // 如果是非接口
        else {
            b = new ByteBuddy()
                    .subclass(superType, ConstructorStrategy.Default.IMITATE_SUPER_CLASS)
                    .name(superType.getName() + proxySuffix)
                    .modifiers(Visibility.PUBLIC);
        }

        // 只代理抽象方法
        b = b.method(isAbstract()
                        // 跳过 toString/hashCode 等Object里面定义的方法
                        .and(not(isDeclaredBy(Object.class)))
                        // 规避编译器合成方法（泛型桥接等）
                        .and(not(isSynthetic()))
                        .and(not(isBridge())))
                .intercept(MethodDelegation.to(new SqlMethodInterceptor(beanFactory)));


        try {
            b = b.defineMethod(proxyMethodInitName, void.class, Visibility.PUBLIC)
                    .intercept(net.bytebuddy.implementation.MethodCall
                            .invoke(SqlFlowInitSupport.class.getDeclaredMethod("init", Object.class))
                            .withThis());
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException(e);
        }

        try (DynamicType.Unloaded<?> unloaded = b.make()) {
            return unloaded
                    .load(superType.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();
        }
    }
}
